package com.zmn.uac.business.impl.oauth.login;

import com.alibaba.fastjson.JSONObject;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.consts.GlobalConsts;
import com.zmn.saas.dubbo.dto.app.AppDRO;
import com.zmn.uac.business.interfaces.oauth.login.BaiduLoginBService;
import com.zmn.uac.business.template.oauth.login.BaseLoginTemplateMode;
import com.zmn.uac.common.constant.AccessTypeConstant;
import com.zmn.uac.common.constant.BaiduUrlConstant;
import com.zmn.uac.common.dio.oauth.login.TempCodeDIO;
import com.zmn.uac.common.dio.oauth.login.UserAuthLoginDIO;
import com.zmn.uac.common.entity.oauth.login.*;
import com.zmn.uac.common.entity.oauth.phone.UserPhoneInfo;
import com.zmn.uac.common.info.UserAuthPhoneInfo;
import com.zmn.uac.common.info.baidu.BaiduMinProgramSessionInfo;
import com.zmn.uac.common.info.baidu.BaiduUserAuthPhoneInfo;
import com.zmn.uac.common.info.baidu.BaiduUserInfo;
import com.zmn.uac.common.utils.PKCS7Encoder;
import com.zmn.uac.util.HttpClientsUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;

/**
 * 类描述：百度登陆业务实现
 *
 * @author xujie
 * @since 2021/06/11 16:58
 */
@Slf4j
@Service
public class BaiduLoginBServiceImpl extends BaseLoginTemplateMode implements BaiduLoginBService {

    private static final String TAG = "百度登陆业务实现";

    @Override
    public ResponseDTO<UserAuthLoginInfo> thirdPartyUserDataAcquisition(UserAuthLoginDIO dio, UserAuthPhoneInfo authPhoneInfo) {
        BaiduUserAuthPhoneInfo baiduUserAuthPhoneInfo = (BaiduUserAuthPhoneInfo) authPhoneInfo;
        String encryptedData;
        try {
            encryptedData = URLDecoder.decode(baiduUserAuthPhoneInfo.getEncryptedData(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("[{}], 百度小程序用户登录失败, url数据解码异常：{}", TAG, e.getMessage(), e);
            return ResponseDTO.fail("百度小程序用户登录失败");
        }
        Integer channelId = dio.getChannelId();
        AppDRO app = Objects.equals(dio.getAccessType(), AccessTypeConstant.SHOP) ? getAppInfoByShopId(dio.getShopId()) : getAppByChannelId(channelId);
        if (Objects.isNull(app)) {
            return ResponseDTO.fail("查询应用为null");
        }

        // 获取百度小程序用户的Session
        ResponseDTO<BaiduMinProgramSessionInfo> baiduMinProgramSessionInfoResponse = getBaiduMinProgramSessionInfo(app.getThirdAppId(), app.getThirdAppSecret(), dio.getCode(), dio.getUuid());
        if (!baiduMinProgramSessionInfoResponse.isSuccess()) {
            return ResponseDTO.fail(baiduMinProgramSessionInfoResponse.getMessage());
        }

        BaiduMinProgramSessionInfo baiduMinProgramSessionInfo = baiduMinProgramSessionInfoResponse.getData();
        BaiduUserInfo baiduUserInfo;
        if (StringUtil.isBlank(authPhoneInfo.getMobile())) {
            ResponseDTO<BaiduUserInfo> baiduUserInfoResponse = decrypt(encryptedData, baiduMinProgramSessionInfo.getSessionKey());
            if (baiduUserInfoResponse.isSuccess()) {
                baiduUserInfo = baiduUserInfoResponse.getData();
            } else {
                baiduUserInfo = new BaiduUserInfo();
            }
        } else {
            baiduUserInfo = BaiduUserInfo.builder().mobile(authPhoneInfo.getMobile()).build();
        }

        UserPhoneInfo userPhoneInfo = UserPhoneInfo.builder().mobile(baiduUserInfo.getMobile()).build();
        Integer gender = (baiduUserInfo.getSex() != null && baiduUserInfo.getSex() == 0) ? baiduUserInfo.getSex() : 3;

        UserInfo info = setUserInfo(baiduMinProgramSessionInfo, baiduUserInfo, gender);
        PlatInfo platInfo = setPlatInfo(dio, app, channelId);
        ThirdAppInfo thirdAppInfo = setThirdAppInfo(app);
        UserOtherInfo userOtherInfo = setUserOtherInfo(baiduUserInfo, baiduMinProgramSessionInfo);

        UserAuthLoginInfo userAuthLoginInfo = setUserAuthLoginInfo(info, userOtherInfo, platInfo, thirdAppInfo, userPhoneInfo);

        return ResponseDTO.success(userAuthLoginInfo);
    }

    @Override
    public ResponseDTO<String> getThirdPartyOpenIdByCode(TempCodeDIO tempCodeDIO, AppDRO app) {
        ResponseDTO<BaiduMinProgramSessionInfo> baiduMinProgramSessionInfoResponse =
                getBaiduMinProgramSessionInfo(app.getThirdAppId(), app.getThirdAppSecret(), tempCodeDIO.getCode(), null);
        if (!baiduMinProgramSessionInfoResponse.isSuccess()) {
            return ResponseDTO.fail(baiduMinProgramSessionInfoResponse.getMessage());
        }
        log.info("获取百度调用响应baiduMinProgramSessionInfoResponse:{}", baiduMinProgramSessionInfoResponse.getData());
        return ResponseDTO.success(baiduMinProgramSessionInfoResponse.getData().getOpenId());
    }

    /**
     * 获取百度小程序用户的Session
     * @param appId
     * @param secret
     * @param code
     * @param uuid
     * @return
     */
    private ResponseDTO<BaiduMinProgramSessionInfo> getBaiduMinProgramSessionInfo(String appId, String secret, String code, String uuid) {
        String requestCodeUrl = String.format(BaiduUrlConstant.CODE_2_SESSION_URL, code.trim(), appId.trim(), secret.trim());
        String jsonResult;
        try {
            jsonResult = HttpClientsUtil.executeGet(requestCodeUrl);
        } catch (Exception e) {
            log.error("[{}], {}, 获取百度小程序用户的Session失败:{}", TAG, uuid, e.getMessage(), e);
            return ResponseDTO.fail("获取百度小程序用户的Session失败");
        }
        log.info("[{}], {}, getPersonalAccessToken, 获取百度小程序用户的Session，json: {}", TAG, uuid, jsonResult);
        BaiduMinProgramSessionInfo baiduMinProgramSession = JSONObject.parseObject(jsonResult, BaiduMinProgramSessionInfo.class);
        if (Objects.isNull(baiduMinProgramSession) || StringUtil.isBlank(baiduMinProgramSession.getSessionKey())) {
            log.error("[{}], {}, 获取百度小程序数据错误, appid：{},secret：{},code：{}", TAG, uuid, appId, secret, code);
            return ResponseDTO.fail("获取百度小程序数据错误");
        }
        return ResponseDTO.success(baiduMinProgramSession);
    }

    /**
     * 对密文进行解密
     *
     * @param encryptedData 需要解密的密文
     * @return 解密得到的明文
     */
    public ResponseDTO<BaiduUserInfo> decrypt(String encryptedData, String sessionKey) {

        byte[] aesKey = Base64.decodeBase64(sessionKey + "=");
        byte[] original;
        // 偏移量
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
            // IvParameterSpec iv = new IvParameterSpec(ivByte);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
            byte[] encrypted = Base64.decodeBase64(encryptedData);
            original = cipher.doFinal(encrypted);
        } catch (Exception e) {
            log.error("[{}], 百度用户信息解密异常One：{}", TAG, e.getMessage(), e);
            return ResponseDTO.fail("百度用户信息解密异常");
        }
        String xmlContent;
        try {
            // 去除补位字符
            byte[] bytes = PKCS7Encoder.decode(original);
            // 分离16位随机字符串,网络字节序和ClientId
            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
            int xmlLength = recoverNetworkBytesOrder(networkOrder);
            xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("[{}], 百度用户信息解密异常Two：{}", TAG, e.getMessage(), e);
            return ResponseDTO.fail("解析百度用户数据错误");
        }
        log.info("[{}], 百度用户信息解密xmlContent：{}", TAG, xmlContent);
        BaiduUserInfo userInfo = JSONObject.parseObject(xmlContent, BaiduUserInfo.class);
        if (userInfo != null && StringUtil.isMobile(userInfo.getMobile())) {
            log.info("[{}], 百度用户信息userInfo：{}", TAG, JSONObject.toJSONString(userInfo));
            return ResponseDTO.success(userInfo);
        } else {
            return ResponseDTO.fail("解析百度用户数据失败");
        }
    }

    /**
     * 还原4个字节的网络字节序
     *
     * @param orderBytes 字节码
     * @return sourceNumber
     */
    private int recoverNetworkBytesOrder(byte[] orderBytes) {
        int sourceNumber = 0;
        int length = 4;
        int number = 8;
        for (int i = 0; i < length; i++) {
            sourceNumber <<= number;
            sourceNumber |= orderBytes[i] & 0xff;
        }
        return sourceNumber;
    }

    /**
     * 设置userInfo
     * @param baiduMinProgramSessionInfo
     * @param baiduUserInfo
     * @param gender
     * @return
     */
    private UserInfo setUserInfo(BaiduMinProgramSessionInfo baiduMinProgramSessionInfo,BaiduUserInfo baiduUserInfo,Integer gender){
        return UserInfo.builder()
                .userId(null)
                .openId(baiduMinProgramSessionInfo.getOpenId())
                .unionId(baiduMinProgramSessionInfo.getUnionId())
                .type(GlobalConsts.NO)
                .realname(StringUtil.isBlank(baiduUserInfo.getNickname()) ? "百度用户" : baiduUserInfo.getNickname())
                .username(null)
                .nickName(baiduUserInfo.getNickname())
                .avatarSrc(baiduUserInfo.getHeadImgUrl())
                .age(null)
                .gender(gender)
                .country(baiduUserInfo.getCountry())
                .province(baiduUserInfo.getProvince())
                .city(baiduUserInfo.getCity())
                .build();
    }

    /**
     * 设置platInfo
     * @param dio
     * @param app
     * @param channelId
     * @return
     */
    private PlatInfo setPlatInfo(UserAuthLoginDIO dio,AppDRO app,Integer channelId){
        return PlatInfo.builder()
                .shopId(dio.getShopId())
                .plat(app.getPlat())
                .channelId(channelId)
                .channelName(getNameByChannelId(channelId))
                .entrance(dio.getEntrance())
                .build();
    }

    /**
     * 设置ThirdAppInfo
     * @param app
     * @return
     */
    private ThirdAppInfo setThirdAppInfo(AppDRO app){
        return ThirdAppInfo.builder()
                .appId(app.getThirdAppId())
                .appName(app.getName())
                .appSecret(app.getThirdAppSecret())
                .build();
    }

    /**
     * 设置UserOtherInfo
     * @param baiduUserInfo
     * @param baiduMinProgramSessionInfo
     * @return
     */
    private UserOtherInfo setUserOtherInfo(BaiduUserInfo baiduUserInfo,BaiduMinProgramSessionInfo baiduMinProgramSessionInfo){
        return UserOtherInfo.builder()
                .language(baiduUserInfo.getLanguage())
                .sessionKey(baiduMinProgramSessionInfo.getSessionKey())
                .build();
    }

    /**
     * 设置UserAuthLoginInfo
     * @param info
     * @param userOtherInfo
     * @param platInfo
     * @param thirdAppInfo
     * @param userPhoneInfo
     * @return
     */
    private UserAuthLoginInfo setUserAuthLoginInfo(UserInfo info,UserOtherInfo userOtherInfo,PlatInfo platInfo,ThirdAppInfo thirdAppInfo,UserPhoneInfo userPhoneInfo){
        return UserAuthLoginInfo.builder()
                .userInfo(info)
                .userOtherInfo(userOtherInfo)
                .platInfo(platInfo)
                .thirdAppInfo(thirdAppInfo)
                .userPhoneInfo(userPhoneInfo)
                .build();
    }
}
